#include <vector>
#include <iostream>
#include "excelpoi.hpp"
#include "util.hpp"

// xls cell types
//enum cell_type {xls_numeric, xls_string, xls_formula, xls_blank, xls_boolean, xls_error};


// java env and VM are static
static JNIEnv* java_poi_env;
static JavaVM* java_poi_vm;

// classes and registered routines are static too

// classes we will use
static jclass cls_WorkBook;
static jclass cls_WorkSheet;
static jclass cls_Row;
static jclass cls_Cell;
static jclass cls_DataFormat;
static jclass cls_CellStyle;
static jclass cls_POIFileSystem;
static jclass cls_FileOutputStream;
static jclass cls_FileInputStream;


// methods we will use

// POIFS
static jmethodID initPOIFS;

// workbook methods
static jmethodID initWorkBook;
static jmethodID initWorkBook_fromPOIFS;
static jmethodID createSheet;
static jmethodID writeWB;
static jmethodID getSheet;
static jmethodID getSheetAt;
static jmethodID getNumberOfSheets;
static jmethodID createFreezePane;

// sheet methods;
static jmethodID freezePlanes;
static jmethodID createRow;
static jmethodID getRow;
static jmethodID getLastRowNum;
static jmethodID setColumnWidth;

// row methods;
static jmethodID getFirstCellNum;
static jmethodID getLastCellNum;
static jmethodID createCell;
static jmethodID getCell;

// cell methods;
static jmethodID setCellType;
static jmethodID setCellValue_double;
static jmethodID setCellValue_formula;
static jmethodID setCellValue_string;
static jmethodID setCellStyle;

static jmethodID getCellType;
static jmethodID getCellValue_double;
static jmethodID getCellValue_string;

// cell style methods
static jmethodID createCellStyle;
static jmethodID setAlignment;

// data format methods
static jmethodID createDataFormat;
static jmethodID setDataFormat;
static jmethodID getFormat;
static jmethodID getBuiltinFormat;

// file stream methods;
static jmethodID openFileOutputStream;
static jmethodID closeFileOutputStream;

static jmethodID openFileInputStream;
static jmethodID closeFileInputStream;

#ifdef POI_DEBUG
#define CHECK_FOR_EXCEPTIONS			\
  if (java_poi_env->ExceptionOccurred()) {	\
    java_poi_env->ExceptionDescribe();		\
    exit(1);					\
  }
#endif

// the names of these functions must match 
// the name of the dll: excelpoi.o or .dll
// do not change them lightly b/c then JNI_init
// will not be called (I learned the hard way)
void R_init_Rexcelpoi(DllInfo *info) {
  // start Java JVM
  if(!JNI_init()) {
    Rprintf("problem loading JVM.\n");
    Rprintf("make sure the POI is in your java CLASSPATH.\n");
  }
}

void R_unload_Rexcelpoi(DllInfo *info) {
  // destroy java JVM
  java_poi_vm->DestroyJavaVM();
}

string CvtJst_to_string(jstring jstr) {
  string cstr;
  if (jstr != 0) { 
    // Now convert the Java String to C++ char array
    // FIXME: this will have to be released later to 
    // a the memory leak
    cstr = java_poi_env->GetStringUTFChars(jstr, 0);
  }
  return cstr;
}


/* initialize the JVM
   need to read the classpath
   from R via: Sys.getenv()
   at this point and put it
   into options[1] below */
int JNI_init() {
  vector<string> jArgs;

  // java args
  string cp1("-Djava.class.path=");
  string cp2(getenv("CLASSPATH")); 

  jArgs.push_back(cp1+cp2);

  JavaVMInitArgs vm_args;
  JavaVMOption options[jArgs.size()];

  for(int i = 0; i < jArgs.size(); i++) {
    options[i].optionString = const_cast<char*>(jArgs[i].c_str());
  }
  vm_args.version = JNI_VERSION_1_4;
  vm_args.nOptions = jArgs.size();
  vm_args.options = options;
  vm_args.ignoreUnrecognized = JNI_TRUE;

  if(JNI_CreateJavaVM(&java_poi_vm, (void**) &java_poi_env, &vm_args)==0) {

    // register the routines we will use
    cls_WorkBook= java_poi_env->FindClass("org/apache/poi/hssf/usermodel/HSSFWorkbook");
    cls_WorkSheet= java_poi_env->FindClass("org/apache/poi/hssf/usermodel/HSSFSheet");
    cls_Row= java_poi_env->FindClass("org/apache/poi/hssf/usermodel/HSSFRow");
    cls_Cell= java_poi_env->FindClass("org/apache/poi/hssf/usermodel/HSSFCell");		

    cls_CellStyle = java_poi_env->FindClass("org/apache/poi/hssf/usermodel/HSSFCellStyle");
    cls_DataFormat = java_poi_env->FindClass("org/apache/poi/hssf/usermodel/HSSFDataFormat");

    cls_POIFileSystem= java_poi_env->FindClass("org/apache/poi/poifs/filesystem/POIFSFileSystem");		
    cls_FileOutputStream = java_poi_env->FindClass("java/io/FileOutputStream");
    cls_FileInputStream = java_poi_env->FindClass("java/io/FileInputStream");


    // POIFS
    initPOIFS = java_poi_env->GetMethodID(cls_POIFileSystem,"<init>","(Ljava/io/InputStream;)V");

    // workbook methods
    initWorkBook = java_poi_env->GetMethodID(cls_WorkBook,"<init>","()V");
    initWorkBook_fromPOIFS = java_poi_env->GetMethodID(cls_WorkBook,"<init>","(Lorg/apache/poi/poifs/filesystem/POIFSFileSystem;)V");
    createSheet = java_poi_env->GetMethodID(cls_WorkBook,"createSheet","(Ljava/lang/String;)Lorg/apache/poi/hssf/usermodel/HSSFSheet;");
    getNumberOfSheets = java_poi_env->GetMethodID(cls_WorkBook,"getNumberOfSheets","()I");
    getSheet = java_poi_env->GetMethodID(cls_WorkBook,"getSheet","(Ljava/lang/String;)Lorg/apache/poi/hssf/usermodel/HSSFSheet;");
    getSheetAt = java_poi_env->GetMethodID(cls_WorkBook,"getSheetAt","(I)Lorg/apache/poi/hssf/usermodel/HSSFSheet;");
    writeWB = java_poi_env->GetMethodID(cls_WorkBook,"write","(Ljava/io/OutputStream;)V");

    createDataFormat = java_poi_env->GetMethodID(cls_WorkBook,"createDataFormat","()Lorg/apache/poi/hssf/usermodel/HSSFDataFormat;");
    createCellStyle  = java_poi_env->GetMethodID(cls_WorkBook,"createCellStyle","()Lorg/apache/poi/hssf/usermodel/HSSFCellStyle;");
		
    // sheet methods
    createFreezePane = java_poi_env->GetMethodID(cls_WorkSheet,"createFreezePane","(II)V");
    createRow = java_poi_env->GetMethodID(cls_WorkSheet,"createRow","(I)Lorg/apache/poi/hssf/usermodel/HSSFRow;");
    getRow = java_poi_env->GetMethodID(cls_WorkSheet,"getRow","(I)Lorg/apache/poi/hssf/usermodel/HSSFRow;");
    getLastRowNum = java_poi_env->GetMethodID(cls_WorkSheet,"getLastRowNum","()I");
    setColumnWidth = java_poi_env->GetMethodID(cls_WorkSheet,"setColumnWidth","(SS)V");

    // row methods
    getFirstCellNum = java_poi_env->GetMethodID(cls_Row,"getFirstCellNum","()S");
    getLastCellNum = java_poi_env->GetMethodID(cls_Row,"getLastCellNum","()S");
    createCell = java_poi_env->GetMethodID(cls_Row,"createCell","(S)Lorg/apache/poi/hssf/usermodel/HSSFCell;");
    getCell = java_poi_env->GetMethodID(cls_Row,"getCell","(S)Lorg/apache/poi/hssf/usermodel/HSSFCell;");


    // cell methods

    // get type
    getCellType = java_poi_env->GetMethodID(cls_Cell,"getCellType","()I");

    // set values
    setCellValue_double  = java_poi_env->GetMethodID(cls_Cell,"setCellValue","(D)V");
    setCellValue_formula = java_poi_env->GetMethodID(cls_Cell,"setCellFormula","(Ljava/lang/String;)V");
    setCellValue_string  = java_poi_env->GetMethodID(cls_Cell,"setCellValue","(Ljava/lang/String;)V");

    // set style
    setCellStyle = java_poi_env->GetMethodID(cls_Cell,"setCellStyle","(Lorg/apache/poi/hssf/usermodel/HSSFCellStyle;)V");

    // get values
    getCellValue_double  = java_poi_env->GetMethodID(cls_Cell,"getNumericCellValue","()D");
    getCellValue_string  = java_poi_env->GetMethodID(cls_Cell,"getStringCellValue","()Ljava/lang/String;");

    // cell style
    setDataFormat = java_poi_env->GetMethodID(cls_CellStyle,"setDataFormat","(S)V");
    setAlignment = java_poi_env->GetMethodID(cls_CellStyle,"setAlignment","(S)V");

    // data format 
    getFormat = java_poi_env->GetMethodID(cls_DataFormat,"getFormat","(Ljava/lang/String;)S");
    getBuiltinFormat = java_poi_env->GetStaticMethodID(cls_DataFormat,"getBuiltinFormat","(Ljava/lang/String;)S");

    // file stream methods
    openFileOutputStream = java_poi_env->GetMethodID(cls_FileOutputStream,"<init>","(Ljava/lang/String;)V");
    closeFileOutputStream = java_poi_env->GetMethodID(cls_FileOutputStream,"close","()V");

    openFileInputStream = java_poi_env->GetMethodID(cls_FileInputStream,"<init>","(Ljava/lang/String;)V");
    closeFileInputStream = java_poi_env->GetMethodID(cls_FileInputStream,"close","()V");

    return 1;
  } else {
    cerr << "Failed to create VM!" << endl;
    return 0;
  }

}

/* Rstring2Jstring: convert a char* to a jstring
 */
jstring make_jstring(char* str) {

  jstring ans = java_poi_env->NewStringUTF(str);
  return ans;
}
/* write an R string to a java cell
 */
void writeCHARSEXP(jobject cell, SEXP r_string) {
  jstring fromR;
  if( r_string==R_NilValue || cell==NULL ) {
    return;
  }

  const char* myString = CHAR(r_string);
  fromR = java_poi_env->NewStringUTF(myString);
  java_poi_env->CallVoidMethod(cell,setCellValue_string,fromR);
  java_poi_env->DeleteLocalRef(fromR);
}
/* write a char* to a java cell
 */
void writeChar(jobject cell, char *charValue) {
  jstring fromR;

  if(charValue==NULL||cell==NULL)
    return;

  fromR = java_poi_env->NewStringUTF(charValue);
  java_poi_env->CallVoidMethod(cell,setCellValue_string,fromR);
  java_poi_env->DeleteLocalRef(fromR);
}

// write a double to a cell
void writeDouble(jobject cell, SEXP r_data, int index) {
	
  if(r_data==R_NilValue||cell==NULL)
    return;

  double value = REAL(r_data)[index];

  // if we have an NA, don't write anything (for now)
  // FIXME: add an option to write NA strings
  if(ISNAN(value)) return;

  java_poi_env->CallVoidMethod(cell,setCellValue_double,value);
}

void writeDouble(jobject cell, double value, int index) {
	
  if(cell==NULL)
    return;

  // if we have an NA, don't write anything (for now)
  // FIXME: add an option to write NA strings
  if(ISNAN(value)) return;

  java_poi_env->CallVoidMethod(cell,setCellValue_double,value);
}

// write an int to a cell
void writeInteger(jobject cell, SEXP r_data, int index) {
  if(r_data==R_NilValue||cell==NULL)
    return;
  java_poi_env->CallVoidMethod(cell,setCellValue_double,static_cast<double>(INTEGER(r_data)[index]));
}

// write a string to a cell
void writeString(jobject cell, SEXP r_data, int index) {
  if(r_data==R_NilValue || cell==NULL) {
    return;
  }
  writeCHARSEXP(cell, STRING_ELT(r_data, index));
}

/* writeCase: write a cell based on what kind of R object it is
   having the case statement for every cell is inefficient, but it's
   the easiest way to get a quick solution, and since we are writing w/ Java
   it probably doesn't make a difference
   cell: preinitialized java cell
   r_data: the R data we are going to write
   index: the index of the particular value we are going to write
*/
void writeCase(jobject cell, SEXP r_data, int index) {
  const double epoch = -2208970800.0;

  if(cell==NULL||r_data==R_NilValue)
    return;
  switch(TYPEOF(r_data)) {
  case REALSXP:
    /*
    if(isPOSIXct(r_data)) {
      double excelDate = round((REAL(r_data)[index] - epoch)/86400) + 2.0;      
      writeDouble(cell,excelDate,index);
    } else {
      writeDouble(cell,r_data,index);
    }
    */
      writeDouble(cell,r_data,index);
    break;
  case LGLSXP:
  case INTSXP:
    writeInteger(cell,r_data,index);
    break;
  case STRSXP:
    writeString(cell,r_data,index);
    break;
  }
}

/*
  writeColNames: write the colnames of an R object into a worksheet
  Sheet = the POI sheet we are writing into
  matrix = the R matrix to write out
  colNmsRow = the row to put the colnames in (zero indexed)
  writeColNms = we have to know whether to write a leading blank for the rownames column
*/
void writeColNames(jobject Sheet,SEXP colNames, int writeRowNms) {

  jobject currentCell, ColNamesRow;

  // can't do it if we don't have colNames
  // or if sheet is NULL pointer
  if(colNames == R_NilValue || !Sheet)
    return;
  // colnames are always in the first row (for now)
  ColNamesRow = java_poi_env->CallObjectMethod(Sheet,createRow,(int)0);

  // check for problem creating row
  if(ColNamesRow==NULL) {
    java_poi_env->DeleteLocalRef(ColNamesRow);
    return;
  }

  for(int i = 0; i < length(colNames); i++) {
    // offset the current cell if we have rowNames
    currentCell = java_poi_env->CallObjectMethod(ColNamesRow,createCell,(short)(i + writeRowNms));
    // only write teh cell if we can create it
    if(currentCell) {
      writeCHARSEXP(currentCell,STRING_ELT(colNames, i));
      java_poi_env->DeleteLocalRef(currentCell);
    }
  }
  java_poi_env->DeleteLocalRef(ColNamesRow);
}

/*
  writeRowNames:
  write SEXP names to the rownames of a worksheet
  Sheet: already initialized worksheet
  rowNames: the CHARSEXP rownames (as a vector)
  rowNmsCol: the column number to put the rownames in (zero indexed)
  writeColNms: have to know whether the colnames will be written, so we can offset the rownames
  by the proper amt
*/
void writeRowNames(jobject Sheet, SEXP rowNames, int writeColNms) {

  jobject currentCell, currentRow;
	    
  if(rowNames == R_NilValue || Sheet==NULL) {
    return;
  }

  // if we do not have strings for rownames
  if(TYPEOF(rowNames)!=STRSXP) {
    for(int i = 0; i < length(rowNames); i++) {
      // offset the rownames by 1 if we are writing the colNames
      // writeColNms should be 1 or 0
      currentRow = java_poi_env->CallObjectMethod(Sheet,createRow,i + writeColNms);

      // rownames are always in the first column (for now)
      currentCell = java_poi_env->CallObjectMethod(currentRow,createCell,(short)0);
      writeCase(currentCell,rowNames,i);
      java_poi_env->DeleteLocalRef(currentCell);
      java_poi_env->DeleteLocalRef(currentRow);
    }
  } else {

    for(int i = 0; i < length(rowNames); i++) {

      // offset the rownames by 1 if we are writing the colNames
      // writeColNms should be 1 or 0
      currentRow = java_poi_env->CallObjectMethod(Sheet,createRow,i + writeColNms);

      // rownames are always in the first column (for now)
      currentCell = java_poi_env->CallObjectMethod(currentRow,createCell,(short)0);
      writeCHARSEXP(currentCell,STRING_ELT(rowNames, i));
      java_poi_env->DeleteLocalRef(currentCell);
      java_poi_env->DeleteLocalRef(currentRow);
    }
	
  }
}

void writeDates2RowNames(jobject workbook, jobject Sheet, SEXP dates, int writeColNms) {

  // Jan 1, 1900 represented as the number of seconds since 1970-01-01
  const double epoch = -2208970800.0;
  jobject currentCell, currentRow;
  jobject style, data_fmt; 
  jstring jdata_fmt_str; 
  short data_fmt_code; 
  int format_count; 

  if(dates == R_NilValue || Sheet==NULL || TYPEOF(dates)!=REALSXP) {
    return;
  }

  // make bigger width for 1st column
  java_poi_env->CallVoidMethod(Sheet,setColumnWidth,static_cast<short>(0),static_cast<short>(2600));

  // fixed format for now
  jdata_fmt_str = make_jstring("yyyy-mm-dd");
                         
  // create a blank the style in the workbook 
  style = java_poi_env->CallObjectMethod(workbook,createCellStyle); 
  // create a blank data fmt in the workbook 
  data_fmt = java_poi_env->CallObjectMethod(workbook,createDataFormat); 
  // get the format code from excel (it's a short) 
  data_fmt_code = java_poi_env->CallShortMethod(data_fmt,getBuiltinFormat,jdata_fmt_str); 

  // if it is not a builtin code
  if(data_fmt_code== -1) {
    data_fmt_code = java_poi_env->CallShortMethod(data_fmt,getFormat,jdata_fmt_str);
  }

  java_poi_env->DeleteLocalRef(jdata_fmt_str); 

  // apply the new format code to the style 
  java_poi_env->CallVoidMethod(style,setDataFormat,data_fmt_code);

  for(int i = 0; i < length(dates); i++) {

    // offset the rownames by 1 if we are writing the colNames
    // writeColNms should be 1 or 0
    currentRow = java_poi_env->CallObjectMethod(Sheet,createRow,i + writeColNms);

    // rownames are always in the first column (for now)
    currentCell = java_poi_env->CallObjectMethod(currentRow,createCell,static_cast<short>(0));		

    // just divide by number of seconds in a day (86400)
    // round to the nearest day (because of DST issues)
    // FIXME: make sure there are no DST problems
    // add 2 because excel has a few days that do not exist
    double excelDate = round((REAL(dates)[i] - epoch)/86400) + 2.0;
    java_poi_env->CallVoidMethod(currentCell,setCellValue_double,excelDate);
    java_poi_env->CallVoidMethod(currentCell,setCellStyle,style);
    java_poi_env->DeleteLocalRef(currentCell);
    java_poi_env->DeleteLocalRef(currentRow);
  }
  java_poi_env->DeleteLocalRef(style);
  java_poi_env->DeleteLocalRef(data_fmt);
}


/* 
   createExcelSheet:
   create an instance of a worksheet with a name
   that we can use to write out R data

   WorkBook = the java workbook in which the sheet will be created
   sheetName = the name of the new sheet
   returns a reference to a java sheet object
*/
jobject createExcelSheet(jobject WorkBook, char* sheetName) {
  //	Rprintf("%s\n",sheetName);

  if(WorkBook==NULL || !strlen(sheetName) )
    return NULL;

  // change the sheet name in to a jstring
  jstring jworksheetName = make_jstring(sheetName);

  // make a new sheet on the workbook
  jobject sheet = java_poi_env->CallObjectMethod(WorkBook,createSheet,jworksheetName);
  java_poi_env->DeleteLocalRef(jworksheetName);
	
  if(!sheet) {
    Rprintf("createExcelSheet: failed to create sheet.\n");
  }

  return sheet;
}

void applyRowFormatsToWorksheet(jobject WorkBook, jobject sheet, SEXP formats, int WCN) {
  jobject style, data_fmt; 
  jstring jdata_fmt_str; 
  short data_fmt_code; 
  int format_count; 
  // the cell styles have to be created in the workbook for some odd reason 
  if(formats==R_NilValue || TYPEOF(formats)!=STRSXP || WorkBook==NULL || sheet==NULL) { 
    return;
  }
  
  // more than one format causes bailout
  format_count = length(formats); 
  Rprintf("fmt len %d\n",format_count);
  if(format_count != 1) {
    return;
  }
  // make our one format 
  Rprintf("format: %s\n",CHAR(STRING_ELT(formats,0)));
  jdata_fmt_str = make_jstring(CHAR(STRING_ELT(formats,0))); 
                         
  // create a blank the style in the workbook 
  style = java_poi_env->CallObjectMethod(WorkBook,createCellStyle); 
  // create a blank data fmt in the workbook 
  data_fmt = java_poi_env->CallObjectMethod(WorkBook,createDataFormat); 
  // get the format code from excel (it's a short) 
  data_fmt_code = java_poi_env->CallShortMethod(data_fmt,getBuiltinFormat,jdata_fmt_str); 

  // if it is not a builtin code
  if(data_fmt_code== -1) {
    data_fmt_code = java_poi_env->CallShortMethod(data_fmt,getFormat,jdata_fmt_str);
  }
  cout << "data_fmt_code: " << data_fmt_code << endl;
  java_poi_env->DeleteLocalRef(jdata_fmt_str); 
  // apply the new format code to the style 
  java_poi_env->CallVoidMethod(style,setDataFormat,data_fmt_code);

  // write style to column 0
  applyStyleToColumnLiteral(sheet, 0, style, WCN);

  java_poi_env->DeleteLocalRef(style); 
  java_poi_env->DeleteLocalRef(data_fmt); 

}

void applyFormatsToWorksheet(jobject WorkBook, jobject sheet, SEXP formats, int WCN, int WRN) {

  jobject style, data_fmt;
  jstring jdata_fmt_str;
  short data_fmt_code;
  int format_count;
  // the cell styles have to be created in the workbook for some odd reason
  if(formats!=R_NilValue && TYPEOF(formats)==STRSXP && WorkBook!=NULL && sheet!=NULL) {
    format_count = length(formats);

    // can't do anything if we don't have any formats
    if(!format_count)
      return;
		
    // if there is only one format then apply it to all cells
    if(format_count==1) {
			
      // make our one format
      jdata_fmt_str = make_jstring(CHAR(STRING_ELT(formats,0)));
			
      // create a blank the style in the workbook
      style = java_poi_env->CallObjectMethod(WorkBook,createCellStyle);
      // create a blank data fmt in the workbook
      data_fmt = java_poi_env->CallObjectMethod(WorkBook,createDataFormat);
      // get the format code from excel (it's a short)
      data_fmt_code = java_poi_env->CallShortMethod(data_fmt,getFormat,jdata_fmt_str);
      java_poi_env->DeleteLocalRef(jdata_fmt_str);
      // apply the new format code to the style
      java_poi_env->CallVoidMethod(style,setDataFormat,(short)data_fmt_code);
      // apply this style to all the cols
      for(int i=0;i < getColCount(sheet);i++) {
	applyStyleToColumn(sheet, i, style, WCN, WRN);
      }

      java_poi_env->DeleteLocalRef(style);
      java_poi_env->DeleteLocalRef(data_fmt);
			
    } else {
      for(int i = 0; i < format_count; i++) {

	jdata_fmt_str = make_jstring(CHAR(STRING_ELT(formats,i)));

	// create a blank the style in the workbook
	style = java_poi_env->CallObjectMethod(WorkBook,createCellStyle);
	// create a blank data fmt in the workbook
	data_fmt = java_poi_env->CallObjectMethod(WorkBook,createDataFormat);
	// get the format code from excel (it's a short)
	data_fmt_code = java_poi_env->CallShortMethod(data_fmt,getFormat,jdata_fmt_str);
	java_poi_env->DeleteLocalRef(jdata_fmt_str);
	// apply the new format code to the style
	java_poi_env->CallVoidMethod(style,setDataFormat,(short)data_fmt_code);
	// apply this style to all the cells in the column			 
	applyStyleToColumn(sheet, i, style, WCN, WRN);
	java_poi_env->DeleteLocalRef(style);
	java_poi_env->DeleteLocalRef(data_fmt);
      }
    }
  }
}

void rowAlignRight(jobject WorkBook, jobject Sheet, int rowNum, int writeRowNms) {
  jobject style;

  if(WorkBook==NULL || Sheet==NULL)
    return;

  style = java_poi_env->CallObjectMethod(WorkBook,createCellStyle);

  // alignment is causing a lot of crashes, so be safe here
  if(style==NULL) {
    Rprintf("Failed to create style in workbook.\nSkipping alignment for this column.\n");
    ExceptionCheck();
    return;
  }
			
  // 3 is for right alignment
  java_poi_env->CallVoidMethod(style,setAlignment,(short)3);
  // Rprintf("%d, %d, %d, %d\n",Sheet,rowNum,style,writeRowNms);
  applyStyleToRow(Sheet, rowNum, style, writeRowNms);

  // FIXME: we might also be freeing this reference in applyStyleToRow
  java_poi_env->DeleteLocalRef(style);
  // Rprintf("done with aln right\n");
}

/* jobject: a reference to the java worksheet
   int: the column number
   char*: a format string
   int writeColNms: are we writing column names in this sheet
   int writeRowNms are we writing row names in this sheet
*/
void applyStyleToColumn(jobject Sheet, int colNum, jobject style, int writeColNms, int writeRowNms) {

  jobject currentCell, currentRow;

  if(Sheet==NULL || style==NULL) {
    return;
  }

  int lastRow = java_poi_env->CallIntMethod(Sheet,getLastRowNum);

  for(int i = 0; i < lastRow; i++) {

    // offset the rownames by 1 if we are writing the colNames
    // writeColNms should be 1 or 0
    currentRow = java_poi_env->CallObjectMethod(Sheet,getRow,i + writeColNms);

    if(currentRow!=NULL) {
      // rownames are always in the first column (for now)
      currentCell = java_poi_env->CallObjectMethod(currentRow,getCell,static_cast<short>(colNum+writeRowNms));

      // can't format a cell that doesn't exist
      if(currentCell!=NULL) {
        java_poi_env->CallVoidMethod(currentCell,setCellStyle,style);
      }
      java_poi_env->DeleteLocalRef(currentCell);
    }
    java_poi_env->DeleteLocalRef(currentRow);
  }
}

void applyStyleToColumnLiteral(jobject Sheet, int colNum, jobject style, int offSet) {

  jobject currentCell, currentRow;

  cout << "colNum: " << colNum << endl;
  if(Sheet==NULL || style==NULL)
    return;

  int lastRow = java_poi_env->CallIntMethod(Sheet,getLastRowNum);

  for(int i = 0; i < lastRow; i++) {
    // offset the rownames by 1 if we are writing the colNames
    // writeColNms should be 1 or 0
    //cout << "row:" << (i + offSet) << endl;
    currentRow = java_poi_env->CallObjectMethod(Sheet, getRow, i + offSet);
    short tmp;
    if(currentRow!=NULL) {
      tmp = java_poi_env->CallShortMethod(currentRow,getFirstCellNum);
      cout << "first cell: " << tmp << endl;
      currentCell = java_poi_env->CallObjectMethod(currentRow,getCell,static_cast<short>(0));
      //currentCell = java_poi_env->CallObjectMethod(currentRow,createCell,static_cast<short>(0));
      cout << "currentRow: " << currentRow << endl;
      cout << "currentCell: " << currentCell << endl;
      if(currentCell!=NULL) {
        java_poi_env->CallVoidMethod(currentCell,setCellStyle,style);
      }
      java_poi_env->DeleteLocalRef(currentCell);
    }
    java_poi_env->DeleteLocalRef(currentRow);
  }
}


void applyStyleToRow(jobject Sheet, int rowNum, jobject style, int writeRowNms) {

  jobject currentCell, currentRow;

  if(Sheet==NULL || style==NULL)
    return;
	
  currentRow = java_poi_env->CallObjectMethod(Sheet,getRow,rowNum);
  //Rprintf("currentRow: %d\n",currentRow);

  // only write the format if the row exists
  if(currentRow==NULL)
    return;

  int lastCell = (int)java_poi_env->CallShortMethod(currentRow,getLastCellNum);
  for(int i = 0; i <= lastCell; i++) {
    // rownames are always in the first column (for now)
    currentCell = java_poi_env->CallObjectMethod(currentRow,getCell,(short)(i+writeRowNms));
    //			Rprintf("current cell%d\n", currentCell);
    // only write the format if the cell exists
    if(currentCell!=NULL) {
      java_poi_env->CallVoidMethod(currentCell,setCellStyle,style);
      java_poi_env->DeleteLocalRef(currentCell);
    }
  }
  java_poi_env->DeleteLocalRef(currentRow);
}



/* writeSheet:
   write an R matrix, dataframe, or vector to a POI Excel sheet
   Sheet: previously initialized sheet in java/poi
   r_object: a matrix, vector, or dataframe in R
   writeColNms: do we write the colnames?
   writeRowNms: do we write the rownames?
*/
int writeSheet(jobject workbook, jobject sheet, SEXP r_object, int writeColNms, int writeRowNms) {

  if(sheet==NULL || r_object==R_NilValue)
    return 0;

  // if we have a dataframe, use a different method
  if(isFrame(r_object)) {
    return writeSheetAsDataFrame(workbook, sheet,r_object, writeColNms, writeRowNms);
  } else if(isTS(r_object)) {
    return writeSheetAsTS(workbook, sheet, r_object, writeColNms);
  } else {
    // we assme it's a regular R object from here on
    // does our input have dimensions?
    SEXP dims;
    PROTECT(dims = GET_DIM(r_object));

    // if this is true ,we have a vector
    // so we should write the sheet as a vector
    if(dims == R_NilValue) {
      UNPROTECT(1);
      return writeSheetAsVector(workbook,sheet,r_object,writeColNms,writeRowNms);
    }

    // error checking the dims
    if(length(dims)!=2) {
      Rprintf("number of dims is greater than 2.\n");
      UNPROTECT(1);
      return 0;
    } else {
      UNPROTECT(1);
      return writeSheetAsMatrix(workbook, sheet,r_object,writeColNms,writeRowNms);
    }
  }
}

/* writeSheetAsMatrix: write an R matrix to a worksheet
   Sheet: previously initialized worksheet
   matrix: an R matrix
   writeColNms: do we write the colnames?
   writeRowNms: do we write the rownames?
*/

int writeSheetAsMatrix(jobject workbook, jobject Sheet, SEXP matrix, int writeColNms, int writeRowNms) {

  jobject cell, row;
  SEXP dimNames, colNames, rowNames;

  // can't write to void sheet
  if(Sheet==NULL || matrix==R_NilValue)
    return 0;

  PROTECT(dimNames = GET_DIMNAMES(matrix));
  PROTECT(colNames = GET_COLNAMES(dimNames));
  PROTECT(rowNames = GET_ROWNAMES(dimNames));

  if(writeColNms && colNames!=R_NilValue) {
    writeColNames(Sheet, colNames, writeRowNms);
  }

  if(writeRowNms && rowNames!=R_NilValue) {
    writeRowNames(Sheet, rowNames, writeColNms);
  }

  // find rows and cols of arguments
  int nc = ncols(matrix);
  int nr = nrows(matrix);
	
  //FIXME: if rownames have already been written, then does the creation of a new row
  // destroy the row with the rowname already writtne into it?
  for(int i = 0; i < nr; i++) {
    // offset row if colNames are written out
    row = java_poi_env->CallObjectMethod(Sheet,createRow,i + writeColNms);
    for(int j = 0; j < nc; j++) {
      cell = java_poi_env->CallObjectMethod(row,createCell,(short)(j + writeRowNms));
      writeCase(cell, matrix, i+j*nr);
      java_poi_env->DeleteLocalRef(cell);
    }
    java_poi_env->DeleteLocalRef(row);
  }
  UNPROTECT(3);
  return 1;
}

int writeSheetAsTS(jobject workbook, jobject Sheet, SEXP matrix, int writeColNms) {

  jobject cell, row;
  SEXP dimNames, colNames, rowNames;

  int writeRowNms = 1;

  // can't write to void sheet
  if(Sheet==NULL || matrix==R_NilValue)
    return 0;

  PROTECT(dimNames = GET_DIMNAMES(matrix));
  PROTECT(colNames = GET_COLNAMES(dimNames));
  if(writeColNms && colNames!=R_NilValue) {
    writeColNames(Sheet, colNames, writeRowNms);
  }

  // find rows and cols of arguments
  int nc = ncols(matrix);
  int nr = nrows(matrix);

  writeDates2RowNames(workbook, Sheet, getDatesSEXP(matrix), writeColNms);

	
  //FIXME: if rownames have already been written, then does the creation of a new row
  // destroy the row with the rowname already writtne into it?
  for(int i = 0; i < nr; i++) {
    // offset row if colNames are written out
    row = java_poi_env->CallObjectMethod(Sheet,createRow,i + writeColNms);
    for(int j = 0; j < nc; j++) {
      cell = java_poi_env->CallObjectMethod(row,createCell,(short)(j + writeRowNms));
      writeCase(cell, matrix, i+j*nr);
      java_poi_env->DeleteLocalRef(cell);
    }
    java_poi_env->DeleteLocalRef(row);
  }
  UNPROTECT(2);
  return 1;
}



/* writeSheetAsVector:
   write a vector to a spreadsheet
   Sheet: java poi sheet that has already been created
   vector: R vector
   startrow: which row to start writing on
   startcol: which col to start writing on
*/
int writeSheetAsVector(jobject workbook, jobject Sheet, SEXP r_vector, int writeColNms, int writeRowNms) {
  jobject row, cell;
  SEXP colName, vecNames;

  if(Sheet==NULL || r_vector==R_NilValue)
    return 0;

  PROTECT(vecNames = getAttrib(r_vector, R_NamesSymbol));

  if(writeRowNms && vecNames!=R_NilValue) {
    writeRowNames(Sheet,vecNames,writeColNms);
  }
	
  if(writeColNms) {

    //FIXME:		colName = PRINTNAME(TAG(r_vector));
    PROTECT(colName = mkChar("vector"));
    row = java_poi_env->CallObjectMethod(Sheet,createRow,0);
    cell = java_poi_env->CallObjectMethod(row,createCell,(short)writeRowNms);
    writeCHARSEXP(cell,colName);
    java_poi_env->DeleteLocalRef(cell);
    java_poi_env->DeleteLocalRef(row);
  }

  for(int i = 0; i < length(r_vector); i++) {
    row = java_poi_env->CallObjectMethod(Sheet,createRow,i+writeColNms);
    cell = java_poi_env->CallObjectMethod(row,createCell,(short)writeRowNms);
    writeCase(cell, r_vector, i);
    java_poi_env->DeleteLocalRef(cell);
    java_poi_env->DeleteLocalRef(row);
  }
  UNPROTECT(1+writeColNms);
  return 1;
}

/* writeSheetAsDataFrame: write an object out as a dataframe
   Sheet: pre initialized java sheet
   dataFrame: R dataframe
   writeColNms: do we write the col names?
   writeRowNms: do we write the row names?
*/
int writeSheetAsDataFrame(jobject workbook, jobject Sheet, SEXP dataFrame, int writeColNms, int writeRowNms) {
  jobject cell, row;
  SEXP colNames, rowNames, currentColumn;

  if(Sheet==NULL || dataFrame==R_NilValue)
    return 0;

  PROTECT(colNames = getAttrib(dataFrame, R_NamesSymbol));
  PROTECT(rowNames = getAttrib(dataFrame, R_RowNamesSymbol));

  if(writeColNms && colNames!=R_NilValue) {
    writeColNames(Sheet, colNames, writeRowNms);
  }

  if(writeRowNms && rowNames!=R_NilValue) {
    writeRowNames(Sheet, rowNames, writeColNms);
  }

  // find rows and cols of arguments
  //  ncols does not work for a dataframe
  int nc = length(dataFrame);
	
  // nrows for a dataframe is the length of the first
  // item in the list, if we have a malformed dataframe
  // then there could be some problems
  int nr = length(VECTOR_ELT(dataFrame,0));

  // FIXME: do we need to do this?
  // create rows if they are not created already
  if(!writeRowNms || (writeRowNms && rowNames==R_NilValue)) {
    for(int i = 0; i<nr; i++) {
      row = java_poi_env->CallObjectMethod(Sheet,createRow,i + writeColNms);
      java_poi_env->DeleteLocalRef(row);
    }
  }

  // for a dataframe, write each col one at a time
  for(int i = 0; i < nc; i++) {
    PROTECT(currentColumn = VECTOR_ELT(dataFrame,i));
    for(int j = 0; j < nr; j++) {
      // row is the element of the current column we are on + the offset if we have colnames
      row = java_poi_env->CallObjectMethod(Sheet,getRow,j + writeColNms);
			
      if(row) {
	// cell is the col we are on + the offset if we have rownames
	cell = java_poi_env->CallObjectMethod(row,createCell,(short)(i + writeRowNms));
	if(cell) {
	  writeCase(cell, currentColumn, j);
	  java_poi_env->DeleteLocalRef(cell);
	}
	java_poi_env->DeleteLocalRef(row);
      }
    }
    // unprotect the most recent currentCol
    UNPROTECT(1);
  }
  UNPROTECT(2);
  return 1;
}


/* write an r_object to an excel file
   r_object can be a list, dataframe, matrix, or vector
   filename = filename of the new excel file
   worksheetName = name of worksheet (if object is not a list)
   writeColNms = 0,1 whether to write the column names
   writeRowNms = 0,1 whether to write the row names
   if the object is a list, then the list names are used
*/
SEXP R2xls(SEXP r_object,
	   SEXP filename,
	   SEXP worksheetName,
	   SEXP writeColNms,
	   SEXP writeRowNms,
	   SEXP formats,
	   SEXP rowNameFormats,
	   SEXP alignColNmsRight,
	   SEXP freezePlane)
{ 	
  jstring jfilename;
  jobject sheet;
  SEXP ans;

  // return 1 if successful 0 if not
  PROTECT(ans = allocVector(LGLSXP, 1));

  if(filename==R_NilValue) {
    Rprintf("Filename is null.\n");
    LOGICAL(ans)[0] = FALSE;
    UNPROTECT(1);
    return ans;
  }

  char* fname = CHAR(STRING_ELT(filename,0));

  // int versions of writeColNms and writeRowNms
  int WCN = INTEGER(coerceVector(writeColNms,INTSXP))[0];
  int WRN = INTEGER(coerceVector(writeRowNms,INTSXP))[0];
  int ALN_RIGHT = INTEGER(coerceVector(alignColNmsRight,INTSXP))[0];
  int FP = INTEGER(coerceVector(freezePlane,INTSXP))[0];

  // check to make sure that JVM loads
  if(!java_poi_env) {
    Rprintf("Java VM failed to start on package load.\n");
    Rprintf("Attempting to start JVM now.\n");
    if(!JNI_init()) {
      Rprintf("can't load JVM.\n");
      LOGICAL(ans)[0] = FALSE;
      UNPROTECT(1);
      return ans;
    }
  }

  // create new workbook
  jobject WorkBook = java_poi_env->NewObject(cls_WorkBook,initWorkBook);

  // being very cautious here
  if(WorkBook==NULL) {
    Rprintf("Failed to initialize workbook.\n");
    LOGICAL(ans)[0] = FALSE;
    UNPROTECT(1);
    return ans;
  }
		

  // if we have a list which is not a dataframe (have to be careful with that one)
  // then write the list elements out 
  if(TYPEOF(r_object)==VECSXP && !isFrame(r_object)) {
    writeList(WorkBook, r_object, WCN, WRN, formats, ALN_RIGHT, FP);
  } else {
    // create the sheet
    sheet = createExcelSheet(WorkBook, CHAR(STRING_ELT(worksheetName,0)));
    ExceptionCheck();
    if(sheet==NULL) {
      Rprintf("Failed to initialize a sheet in the workbook.\n");
      LOGICAL(ans)[0] = FALSE;
      UNPROTECT(1);
      return ans;
    }

    // write the sheet
    if(!writeSheet(WorkBook, sheet, r_object, WCN, WRN)) {
      java_poi_env->DeleteLocalRef(sheet);
      Rprintf("R2xls: failed to write sheet.\n");
      LOGICAL(ans)[0] = FALSE;
      UNPROTECT(1);
      return ans;
    }

    // POI has a bug, this does not work
    // write the formats
    /*
    if(rowNameFormats!=R_NilValue && WRN) {
      applyRowFormatsToWorksheet(WorkBook,sheet,rowNameFormats, WCN);
    }
    */

    if(formats!=R_NilValue) {
      applyFormatsToWorksheet(WorkBook,sheet,formats, WCN, WRN);
    }

    if(ALN_RIGHT) {
      rowAlignRight(WorkBook, sheet, 0, WRN);
    }

    if(FP) {
      freeze(sheet, WCN, WRN);
    }
		
    java_poi_env->DeleteLocalRef(sheet);
  }
	
  // make a new jstring to hold the filename
  jfilename = java_poi_env->NewStringUTF(fname);
  if(!jfilename)
    error("could not create filename.");

  // create the output stream
  jobject FileOutStream = java_poi_env->NewObject(cls_FileOutputStream,openFileOutputStream,jfilename);
  ExceptionCheck();
  java_poi_env->DeleteLocalRef(jfilename);

  if(!FileOutStream) {
    error("could not create file");
  }

  // write the excel file out
  java_poi_env->CallVoidMethod(WorkBook,writeWB,FileOutStream);
  ExceptionCheck();
  java_poi_env->DeleteLocalRef(WorkBook);

  // close the output stream
  java_poi_env->CallVoidMethod(FileOutStream,closeFileOutputStream);
  ExceptionCheck();
  java_poi_env->DeleteLocalRef(FileOutStream);
	
  LOGICAL(ans)[0] = TRUE;
  UNPROTECT(1);
  return ans;

}

/* writeList: write an R list to a workbook.  each list element is written as
   a separate page
   workbook: previously initialized workbook into which we are writing
   r_list: list of R objects
   writeColNms: do we write the col names?
   writeRowNms: do we write the row names?
   formats: list of (list of strings specifying the excel formats) FIXME: test for this
*/
int writeList(jobject workbook, SEXP r_list, int writeColNms, int writeRowNms, SEXP formats, int ALN_RIGHT, int FP) {
	
  SEXP listNames;
  jobject sheet;

  if(workbook==NULL || r_list==R_NilValue)
    return 0;

  PROTECT(listNames = getAttrib(r_list, R_NamesSymbol));

  if(listNames == R_NilValue) {
    // FIXME: add code to gen the sheetnames
    // names <- paste("sheet",1:total_sheets) for instance
    Rprintf("this list has no names.  need sheet names to write the workbook.");
    UNPROTECT(1);
    return 0;
  }

  for(int i = 0; i < length(r_list); i++) {
    sheet = createExcelSheet(workbook, CHAR(STRING_ELT(listNames,i)));

    if(!writeSheet(workbook, sheet, VECTOR_ELT(r_list,i), writeColNms, writeRowNms)) {
      Rprintf("failure to write sheet %d.\n",i+1);
      java_poi_env->DeleteLocalRef(sheet);
    } else {
      //Rprintf("done w/ sheet writing.\n");
      // write formats
      // FIXME: a better way to do this is to create an array of sheets (jobjects)
      // then save a handle to each sheet in the array, that way we can take this
      // format writer outside the sheet writer loop
      if(formats!=R_NilValue) {
	if(TYPEOF(formats)==VECSXP && length(formats)==length(r_list)) {
	  applyFormatsToWorksheet(workbook,sheet,VECTOR_ELT(formats,i), writeColNms, writeRowNms);
	} else if(TYPEOF(formats)==STRSXP) {
          applyFormatsToWorksheet(workbook,sheet,formats, writeColNms, writeRowNms);
        } else {
            Rprintf("Problem with formats.\nFormats should be a string vector or a list equal in length to x.\n");
        }
      }

      if(ALN_RIGHT) {
	rowAlignRight(workbook, sheet, 0, writeRowNms);
      }

      if(FP) {
	freeze(sheet,writeColNms,writeRowNms);
      }

      if(sheet) {
	java_poi_env->DeleteLocalRef(sheet);
      }

    }
  }

  UNPROTECT(1);
  return 1;
}

SEXP readColNames(jobject Sheet, int colNamesRow) {

  jobject currentCell;
  jstring currentColName;
  SEXP colNames;
  jobject namesRow = java_poi_env->CallObjectMethod(Sheet,getRow,colNamesRow);

  // FIXME:	
  // we need to know if there are rownames, because if there are we have to offset
  // the colnames by 1 (there should be a blank above the rownames)


  if(namesRow) {
    // take the length of the first row as the number of cols
    // remember to offset by rownames later
    short numCols = java_poi_env->CallShortMethod(namesRow,getLastCellNum) + 1; // (zero indexed)

    // alloc vector to hold colnames
    PROTECT(colNames = allocVector(STRSXP, (int)numCols));

    // loop through cols and read colnames
    for(short i = 0; i < numCols; i++) {
      currentCell = java_poi_env->CallObjectMethod(namesRow,getCell,i);
      currentColName = (jstring) java_poi_env->CallObjectMethod(currentCell,getCellValue_string);
      SET_STRING_ELT(colNames,(int)i,mkChar((const char*)CvtJst_to_string(currentColName).c_str()));
    }
    UNPROTECT(1);
    return colNames;
  } else {
    UNPROTECT(1);
    return R_NilValue;
  }
}

SEXP readRowNames(jobject sheet, int colNms) {
  // FIXME
  return R_NilValue;
}

/* get the sheet called: sheetName
   from the workbook: WorkBook
*/


/* filename: filename to read
   workSheetNames: list of worksheets to read (NA means all of them)
   colNms: do we have col names
   rowNms: do we have row names
   (the colnames and row names are assumed to be in the first row and first col)
*/

//FIXME: replace shouldn't have replaced the colnames stuff here:
SEXP xls2R(SEXP filename, SEXP workSheetNames, SEXP colNms, SEXP rowNms) {

  SEXP ans;
  // native int versions of colNms and rowNms
  int CN, RN;

  // check to make sure that JVM loads
  if(!java_poi_env) {
    Rprintf("Java VM failed to start.\n");
    Rprintf("attempting to start JVM now.\n");
    if(!JNI_init()) {
      Rprintf("can't load JVM.\n");
      return R_NilValue;
    }
  }

  CN = INTEGER(coerceVector(colNms,INTSXP))[0];
  RN = INTEGER(coerceVector(rowNms,INTSXP))[0];
	
  // make a new jstring to hold the filename
  jstring jfilename = make_jstring(CHAR(STRING_ELT(filename,0)));

  // create the output stream
  jobject FileInputStream = java_poi_env->NewObject(cls_FileInputStream,openFileInputStream,jfilename);

  // create the POIFS
  jobject POIFS = java_poi_env->NewObject(cls_POIFileSystem,initPOIFS,FileInputStream);
	
  // create a workbook from the POIFS instance
  jobject WorkBook = java_poi_env->NewObject(cls_WorkBook,initWorkBook_fromPOIFS,POIFS);

  // get sheet count
  int numSheets = java_poi_env->CallIntMethod(WorkBook,getNumberOfSheets);

  if(numSheets==1) {
    PROTECT(ans = readSheet(java_poi_env->CallObjectMethod(WorkBook,getSheetAt,(jint)0),CN,RN));
  } else {
    PROTECT(ans = readMultiSheets(WorkBook,workSheetNames,CN,RN));
  }
  UNPROTECT(1);
  return ans;
}

// read a named range (or list of named ranges): rangeName
// from an excel workbook: filename
SEXP readNamedRange(SEXP filename, SEXP rangeName) {
  warning("Not implemented yet.\n");
  return R_NilValue;
}

SEXP readMultiSheets(jobject workbook, SEXP workSheetNames, int colNms, int rowNms) {
  SEXP ans;
  int sheetsToRead;
  jobject currentSheet;

  // if workSheetNames is NULL, then we read all the sheets
  if(workSheetNames==R_NilValue) {
    sheetsToRead = java_poi_env->CallIntMethod(workbook,getNumberOfSheets);
  } else {
    sheetsToRead = length(workSheetNames);
  }

  // allocate a list for the sheets we will read
  PROTECT(ans = allocVector(VECSXP, sheetsToRead));
  for(int i = 0; i < sheetsToRead; i++) {
    // make sure to check that name is valid
    currentSheet = java_poi_env->CallObjectMethod(workbook,getSheet,make_jstring(CHAR(STRING_ELT(workSheetNames,i))));
    if(currentSheet) {
      SET_VECTOR_ELT(ans, i, readSheet(currentSheet,colNms,rowNms));
    } else {
      SET_VECTOR_ELT(ans, i, R_NilValue);
    }
  }
  UNPROTECT(1);
  return ans;
}

SEXP readSheet(jobject sheet, int colNms, int rowNms) {
  SEXP colNames, rowNames, ans;
  jobject currentRow, currentCell;

  // get the row count
  int lastRow = java_poi_env->CallIntMethod(sheet,getLastRowNum);

  int numRows = lastRow++; // since rows are zero indexed

  if(numRows<=0) {
    return R_NilValue;
  }

  // read colnames
  if(colNms) {
    PROTECT(colNames = readColNames(sheet,rowNms));
  }

  // read rownames
  if(rowNms) {
    PROTECT(rowNames = readRowNames(sheet,colNms));
  }

  int numCols = guessNumberOfCols(sheet);
	
  // now that we have both rows and cols, we can alloc a matrix
  PROTECT(ans = allocMatrix(REALSXP,numRows,numCols));
  double* ans_ptr = REAL(ans);

  // loop through rows and read data
  for(int i = 0; i < numRows; i++) {
    currentRow = java_poi_env->CallObjectMethod(sheet,getRow,i);
    for(short j = 0; j < numCols; j++) {
      currentCell = java_poi_env->CallObjectMethod(currentRow,getCell,j);
      // put it into the matrix in the proper place
      ans_ptr[i+numRows*(int)j] = java_poi_env->CallDoubleMethod(currentCell,getCellValue_double);
    }
  }
  // this depends on how many objects
  // were protected
  UNPROTECT(1+colNms+rowNms);
  return ans;
}


// try to guess the number of cols based on the first and second row
int guessNumberOfCols(jobject sheet) {
  short count1, count2, numCols;

  count2 = 0;

  jobject firstRow = java_poi_env->CallObjectMethod(sheet,getRow,0);
  jobject secondRow = java_poi_env->CallObjectMethod(sheet,getRow,1);

  // find the last contiguous cell in the first row
  count1 = findLastContiguous(firstRow);

  // find the last contiguous cell in the second row
  // if we have a second row
  if(secondRow) {
    count2 = findLastContiguous(secondRow);
  }

  // if count1 and count2 agree, then we are good
  // otherwise, return the lower of the two
  // if we don't have a count2, then return count1
  if(count2) {
    if(count2==count1) {
      numCols = count2;
    } else {
      numCols = (count1<count2 ? count1:count2);
    }
  } else {
    numCols = count1;
  }
	 
  numCols++; // since cols are zero indexed

  return (int)numCols;
}

int findLastContiguous(jobject row) {

  short count = 0;
  while(java_poi_env->CallObjectMethod(row,getCell,count)) {
    count++;
  };
  return count;
}

void freeze(jobject sheet, int WCN, int WRN) {
  // if sheet is valid, then apply the freeze
  // only makes sense to freeze if we have one of colnms or rownms
  if(sheet!=NULL && (WRN||WCN) )
    // WCN and WRN reversed on purpose
    java_poi_env->CallVoidMethod(sheet,createFreezePane,WRN,WCN);
}


SEXP pDFdims(SEXP dataFrame) {

  int nc = length(dataFrame);
  int nr = nrows(dataFrame);
  Rprintf("nrows: %d\n",nr);
  Rprintf("ncols: %d\n",nc);
  return R_NilValue;
}

void ExceptionCheck() {
  if(java_poi_env->ExceptionOccurred()) {
    java_poi_env->ExceptionDescribe();
    java_poi_env->ExceptionClear();
  }
}

// find out how man cols are in the sheet
int getColCount(jobject sheet) {
  int nc_row1, nc_row2;
  jobject row1, row2;

  if(sheet==NULL)
    return -1;

  // test a couple of rows to check for consistency
  row1 = java_poi_env->CallObjectMethod(sheet,getRow,0);
  row2 = java_poi_env->CallObjectMethod(sheet,getRow,1);

  if(row1) {
    nc_row1  = (int)java_poi_env->CallShortMethod(row1,getLastCellNum);
    java_poi_env->DeleteLocalRef(row1);
  } else{
    nc_row1 = 0;
  }

  if(row2) {
    nc_row2  = (int)java_poi_env->CallShortMethod(row2,getLastCellNum);
    java_poi_env->DeleteLocalRef(row2);
  } else {
    nc_row2 = 0;
  }

  // both equl, return either
  if(nc_row1 && nc_row2 && nc_row1==nc_row2)
    return nc_row1;

  // not equal return error condition
  if(nc_row1!=nc_row2)
    return -1;

  // only have 1, so return it
  if(nc_row1)
    return nc_row1;

  // default conidtion
  return -1;
}
